!MultiDimensionalArray class methodsFor: 'instance creation'!
new
"Create a zero-dimensional array."
^self new: #()!
new: anArray
"Create a new multidimensional array defined by anArray of subscripts."
"MultiDimensionalArray new: (Array with: (1 to: 3) with: #(a b c))."
^super new subscripts: anArray fill: nil!
new: anArray withAll: element
"Create a new multidimensional array filled with 'element'."
^super new subscripts: anArray fill: element! !
Model subclass: #Table
instanceVariableNames: 'format form selection '
classVariableNames: ''
poolDictionaries: ''
category: 'Benchmarks'!
Table comment:
'A general facility for the graphical display and manipulation of a table of information. Currently only strings, and numbers in a fixed point format, can be displayed.
'!
!Table methodsFor: 'opening'!
openWithLabel: aString
"Display the table in a StandardSystemView."
| view topView |
view _ ScrollableFormView new model: self.
topView _ StandardSystemView
model: self
label: aString
minimumSize: 100 @ 100.
topView
addSubView: view
in: (0 @ 0 extent: 1 @ 1)
borderWidth: 1.
topView controller open! !
!Table methodsFor: 'accessing'!
boxOf: anInteger
"Get the rectangle that can be selected on row anInteger. Override if facility required."
^0@0 extent: 0@0!
data: data
"Set the data in the table, displaying it on the form."
| colForms colForm type col charHeight x y item string alignLeft disp newColForm |
charHeight _ TextStyle default lineGrid.
colForms _ Array new: format size.
1 to: format size do:
[:index |
col _ format at: index.
type _ col at: 2.
colForm _ Form extent: 0 @ (charHeight * data size).
"format = #((2 String) (1 Decimal 2)): a string (in font 1) plus a
number with 2 digits after the decimal point (in font 2). The data
must be an array of (string, number) pairs."
| view topView |
view _ ScrollableFormView new model: ((self new format: anArray)
data: a2DArray).
topView _ StandardSystemView
model: self
label: 'A table'
minimumSize: 100 @ 100.
topView
addSubView: view
in: (0 @ 0 extent: 1 @ 1)
borderWidth: 1.
topView controller open
"Table
format: #((1 String) (2 Decimal 3))
data: #(('Hi there' 42) ('More text' 5.77777) ('A long line of text' 1))"! !
MultiDimensionalArray subclass: #TimingAnalysis
instanceVariableNames: 'inf groups '
classVariableNames: 'Count IntervalSize Status '
poolDictionaries: ''
category: 'Benchmarks'!
TimingAnalysis comment:
'This class generates a graphical display of the amount of time spent within categories of methods. At any one time, the processor may be within several of these categories, due to nesting. The TimingAnalysis interrupts the processor several times to determine the approximate proportions of time spent within groups of categories. Unlike the standard profiling tool "MessageTally spyOn:", this class yields useful results with recursive code.
An AnalysisView displays this information graphically. The categories of methods the processor is in at a particular time are shown as a series of blocks horizontally across the view. Time is shown vertically, but the view does not indicate the order in which events took place.
The user must specify the horizontal position of each category in the view. It is convenient to show mutually exclusive categories at the same position, and lower level categories to the right, as this produces best results. If two categories at the same position are not mutually exclusive, the higher level one takes priority.
An array is used to specify analysis information. It contains a number of sub-arrays which each correspond to a category. Each sub-array contains a list of methods, a horizontal position number (from 1 at the left-hand-side), and a category name which is displayed in the view. The list of methods is itself an array, which contains a number of selectors and/or (class,selector) pairs.
For example:
#(
((factorial) 1 factorial)
(((Integer *) =) 2 arithmetic)
)
has two categories (factorial to the left of arithmetic).
Note that the processor never interrupts primitive methods.
A TimingAnalysis is often used from a BenchmarkSeries; see that class and FactorialBenchmarks for examples.
'!
!TimingAnalysis methodsFor: 'accessing'!
info: info
"Set the analysis information."
inf _ info.
groups _ nil! !
!TimingAnalysis methodsFor: 'opening'!
openWithLabel: label
"Open an AnalysisView."
groups _ nil.
AnalysisView openOn: self label: label! !
!TimingAnalysis methodsFor: 'analysis'!
analyse: aBlock
"Analyse the evaluation of aBlock, storing results in the array.
Return an estimate of the time taken."
| myDelay active time timer observedProcess action count analysisContext |
!TimingAnalysis class methodsFor: 'class initialization'!
initialize
"TimingAnalysis initialize"
IntervalSize _ 20
"Note that the interval size is rather greater in practice, since the processor interrupts less frequently."! !
TimingAnalysis initialize!
Table subclass: #BenchmarkSeries
instanceVariableNames: 'timings version status analysis '
classVariableNames: ''
poolDictionaries: ''
category: 'Benchmarks'!
BenchmarkSeries comment:
'This is a benchmarking and profiling tool, designed as an aid to the development of an efficient application. It can be used to compare execution times of different "versions" of the application, and also to identify code most suitable for optimisation.
A series of benchmarks is used to test a specific application. These benchmarks are defined as methods within the "benchmarks" protocol of a subclass of this class (BenchmarkSeries). Class FactorialBenchmarks provides an example series of benchmarks. Each benchmark method is of the form:
"Initialization"
self time: ["code to be timed"].
self check: ["Validating: code to check if result was correct, yielding a boolean"].
"Finishing"
where "Initialization", "Validating" and "Finishing" are optional pieces of Smalltalk code. Additionally, methods "initialize" and "finish" may be overridden to perform actions applicable to all benchmarks.
This tool can be used to compare different versions of the application. The benchmarks can of course be timed, the application changed, and the benchmarks timed again. Alternatively, if different versions of the application are present in the image, their times can be compared. The instance variable "version" is used for this purpose; it is typically a symbol (or nil if only one version). Benchmarks test the value of the instance variable and execute code accordingly.
An instance of class "BenchmarkSeries" stores execution times for some (or all) of the benchmarks in the series, for a given version. The "time:for:" method is usually used to create such an instance and display results in a view; see the examples in class FactorialBenchmarks. Use the left button of the mouse to scroll and select a particular benchmark from the table of results, and the left button to bring up a menu. The cursor keys can also be used.
Various options are provided to customise this benchmark tool for a particular application. These options are modified by overriding the following methods:
minTime: Yields the minimum amount of time to be spent performing a benchmark. The benchmark is timed several times if necessary. The default is 30 seconds, which usually yields results to an accuracy of at least 10% (depending on amount of paging etc.)
maxTimes Yields the maximum number of times a benchmark is to be timed (default 500). Used to avoid spending too long on very quick benchmarks with large initialization overheads.
trace: Produce some output in the Transcript, by default.
stringFromVersion: versionFromString:do:
A version can be any object; override if anything other than symbols (or nil).
analysisInfo The information describing how benchmarks are analysed, using the "analyse" option from the menu. See TimingAnalysis for more information.
See class FactorialBenchmarks. Happy benchmarking!!
'This class provides three benchmarks to test three versions of "factorial". Spot the difference between "cached2" and "cached" (the more efficient "cached2" was developed with the help of this benchmarking tool!!)
a) the facility to scroll the form in the view (using the red button).
b) the capability to select items in the view (also using the red button), move about selections (using the cursor keys), and bring up a menu (using the yellow button).'!
'A ScrollableFormView displays a form or part of a form. Its instance variable ''offset'' contains the position of the form relative to a central position in the view.'!